昨天我們先讓確定性工具(靜態分析、供應鏈、SAST)產生可重播的第一層證據。
今天開始處理工具無法單獨回答的部分:程式語意、路徑可達性,以及團隊自己的取捨。
那就繼續吧~
在真正進入深度檢查的條列清單前,我會先讓一個未載入本 Skill 檢查清單的獨立 subagent 做第一輪審查,目的是降低九面向清單造成的定錨。因為我們沒辦法叫一個已經讀過 A~I 規則的模型「假裝沒讀過、自由地看」,那種自由是假的。
所以第一輪審查被結構性外包給一個未載入本 Skill 檢查清單的 subagent(ncr-fresh-eyes),並設定三條 context 隔離規則:prompt 裡不准有 category 提示、不准有 severity 分類法、不准有先前 findings 或 scanner digest。它回傳的是待複驗的線索,不是可以直接寫入報告的結論。派不出去時寧可跳過並揭露,也不准主 agent inline 模擬,因為主 agent 已經讀過後面的規則。
在進入九個審查類型之前,我會先做一次「關鍵操作與入口盤點」(Critical Operation Enumeration)。白話來說,就是先列出這次變更可能觸發哪些會改變重要狀態、寫入敏感資料,或造成不可逆後果的操作;再把所有能呼叫它的 API、背景工作、排程、CLI 等入口逐條找出來。只有每條路徑都通過應有的驗證、授權或業務不變條件,才算過關;找不齊時,也不能宣稱這次審查是安全的。之所以需要這道閘門,是因為「找到一個 safety net 就停手」是審查時常見的盲點。
safety net 描述的是一種 reviewer(人和 AI 都會犯)的注意力失敗模式:
你在審一個危險操作(例如「把問卷標成已送出」),心裡的問題是「這有沒有防護?」。
你順著 code 找,也確實找到了一處防護:比如某個呼叫路徑上有跑驗證,大腦就把這個擔憂標記為「已解決」,停止搜尋。
但正確的問題從來不是「存不存在一張安全網」,而是「每一條通往這個操作的路徑是不是都會經過安全網」。
更直接的比喻是:
檢查大樓防火作為,找到一支滅火器就宣布安全。大樓有三個出入口,其中兩個沒有。滅火器「存在」和「每條逃生路線都有覆蓋」很明顯是兩回事。
每一類都要給獨立的 ✅、❌ 或 —(不適用,而且要寫明為什麼不適用):不能含糊帶過,這就是報告裡九宮格總評的資料來源。
在進入清單之前,先給你一把讀它的尺。
底下 A 到 I 一共五十幾條,但它們的份量不一樣。大部分是通用判準,成熟工具與模型通常能提供一部分基線覆蓋;我仍把它們列出來,要求審查流程逐項確認,不能因為模型「應該知道」就當作已經檢查過。
這份完整清單裡,我特別標出一組換一家公司就不一定成立的規則。 它們不是通用知識,而是我從實作、審查與踩坑中歸納出來的設定值:我接受了哪些風險、原則衝突時誰優先,以及哪些情境在醫療環境要升級處理。模型再強,也不會憑空知道我在這些取捨上的立場。
這些規則前面我加了一個 ★。
沒有 ★ 的通用判準仍然要檢查,只是可以先借助 linter 與模型建立基線,再確認實際 coverage 與證據。真正需要你投入最多判斷的,是把你們家對應的規則找出來,那才是 Day 3 說的「心中的一把尺」。
本文的
Critical是我在這份 Skill 裡定義的合併門檻:代表這個問題在我的審查政策下必須先處理,不等同 CVSS,也不是宣稱所有團隊都該給它相同的風險等級。
涵蓋以下面向:
pathlib;既有 os.path 若更清楚,或遷移沒有實際收益,不必為改而改涵蓋以下面向:
★ 若有衝突時優先序:KISS > DRY > YAGNI
KISS、DRY、YAGNI 這三個都是 Coding 時的重要原則:
KISS (Keep It Simple, Stupid) 保持簡單:寫出好懂、不複雜的程式碼。減少負擔:讓其他人也能一眼看懂。
DRY (Don't Repeat Yourself) 不要重複:相同的邏輯只寫一次。方便維護:改一個地方就全部更新。
YAGNI (You Aren't Gonna Need It) 不要過度設計:現在不需要的功能就別做。拒絕想像:不要為「以後可能用到」先寫程式。
★ 最高優先,但用風險管理思維評估,不是一律否決。
風險管理,最近也有上 ISO 27001 LA 課程,裡面整理了四個因應對策:
- 風險接受:決定不採取額外的控制措施,由組織自行承擔該風險
- 風險分擔:與第三方簽訂合約或協議,將風險的部分或全部潛在財務/營運影響分攤出去。
- 風險修改:透過實施控制措施(Controls),來降低風險發生的機率或減少發生後的衝擊程度。這是實務中最普遍採用的處置方式。
- 風險避免:完全停止或取消引發該風險的業務活動、技術方案或作業流程,從根源消除風險來源。
我在 code review 時使用的參考指標,包含
資料敏感度、網路環境與既有防禦,再綜合實際情境判斷適合採取Accept/Transfer/Avoid/Mitigate。
以下這些是一定要處理的:
eval/os.system:在這份 Skill 裡一律列為 Critical;這是我刻意採取的保守合併政策,不是業界通則另有以下規則:
curl;其他情境可用靜態證據、測試或最小重現腳本)+ blast radius(醫療觸發時必須明講 PHI 代價)+具體修法。PoC 只能在受控環境使用消毒資料驗證,切勿對真實資料或正式環境執行破壞性操作。以下有豁免情境:
任何 LLM 看到「憑證出現在專案設定裡」都可能直接喊 Critical!但在這個情境裡,這把 token 唯讀、只能拉取內部套件,而且只能在內網使用。它的權限邊界是下載套件;這是我評估後接受的風險,不是沒有看見憑證存在。
涵蓋:
- 分隔,勿用 underscore _。尤其後端 API URL 應全小寫 → Critical(團隊硬慣例)@jwt_required 只證明你是誰,不證明你能碰這筆資源上面那張清單,每一條的份量看起來都一樣。實際上不是:有些是踩過才補上的,有些是只有在我們這裡才成立的。挑三條講清楚它們為什麼在那裡,以及它們會冤枉誰。
同一份清單裡「註解只複述 code」我只建議刪掉,這條卻是 Critical。差別在成本落在誰身上。
審查到這裡的時候,我已經花力氣比對過簽名跟 docstring、確認它真的不一致了。如果這時候放過,那份力氣就只買到我自己知道。下一個人、下一個 agent 讀到這個函式,會照著那份過時的說明去理解、去呼叫,然後在同一個地方摔一跤,花一樣的時間、付一樣的成本,把同一個問題重新排除一次。
發現的成本已經付過了。不修,等於讓它被重複支付。
會誤傷的情況:純粹補型別、不動行為的 commit。那種 diff 裡簽名確實變了但語意沒變,這時候要看的是 docstring 描述的行為還成不成立,不是欄位有沒有逐字對上。
這是我們團隊的硬慣例,不是業界共識。我把它寫進 skill 並判 Critical,是因為它是一個設定值,不是一項知識:模型再強也不會知道我們選了 dash 而不是 underscore。這種東西不寫下來,它就不存在。
會誤傷的情況:路徑不是我們定的時候。串接第三方 API、或者這個 endpoint 是沿用既有系統的舊風格,那就不適用;規約管的是我們自己新開的路徑。
醫師一次開立五種藥,這五筆資料代表的是同一個完整用藥決策,不是五筆互不相干的寫入。如果批次處理到第三筆時失敗,系統只留下前兩種,卻仍讓下游把它當成完整處方,病人接收到的就不是醫師原本開立的治療方案。
所以這裡判 Critical。原子性保護的不是資料筆數,而是醫療決策的完整性。如果業務刻意允許 partial success,就必須讓「尚未完成」成為明確狀態,並定義重試與恢復方式,不能讓部分結果冒充完整結果。
會誤傷的情況:不同資料本來就允許獨立成功,而且系統已明確定義 partial success、重試與恢復契約。這時不適用上述「完整用藥決策不可被拆開」的 Critical 判定。
今天先擋掉兩個結構性的問題:用未載入檢查清單的獨立 subagent 降低定錨,再用關鍵操作與入口盤點,擋掉「找到一張安全網就收手」。然後才走進九面向清單的前五類。
你也可以開始盤點自己 in-house 的規則:哪些是通用檢查,哪些是只有你們團隊才知道的取捨?把後者的理由、適用範圍與例外一起寫進 Skill,這份清單才會真的變成你們自己的審查標準。
明天把九面向的後四類走完(F 資料取用與資料庫、G 測試、H 非 Python 檔、I 回溯分析),再補上 CodeGraph 這個結構視角、subagent 的命名規約,以及進報告前的最後一道閘。